﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Interfaces;

namespace Models
{
    public class DomæneCollection
    {
        #region Fields

        private List<Afdeling> _afdelinger;
        private List<Enhed> _enheder;
        private List<Software> _software;

        #endregion

        #region Creation

        public DomæneCollection()
        {
            try
            {
                LavObjektTilknytninger();
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        // metode udelukket brugt til unit testing
        public static DomæneCollection CreateTest(List<Afdeling> afdelinger, List<Enhed> enheder)
        {
            var collection = new DomæneCollection
            {
                _afdelinger = new List<Afdeling>(afdelinger),
                _enheder = new List<Enhed>(enheder)
            };
            return collection;
        }

        #endregion

        #region Properties

        // Henter alle Afdelinger
        public List<Afdeling> AlleAfdelinger { get { return _afdelinger; } }

        // Henter alle diverse enheder
        public List<Diverse> AlleDiverseEnheder
        {
            get
            {
                return _enheder.Where(e => e is Diverse).Cast<Diverse>().ToList();
            }
        }

        // Henter alle computer enheder
        public IEnumerable<Computer> AlleComputerEnheder
        {
            get
            {
                foreach (Enhed enhed in _enheder)
                {
                    if (enhed is Computer)
                    {
                        yield return (Computer)enhed;
                    }
                }
            }
        }

        // Henter alle mobile enheder
        public IEnumerable<Mobil> AlleMobileEnheder
        {
            get
            {
                return _enheder.Where(e => e is Mobil).Cast<Mobil>();
            }
        }

        // Henter alt software
        public IEnumerable<Software> AltSoftware
        {
            get { return _software; }
        }

        // Henter alle enheder
        public List<Enhed> AlleEnheder
        {
            get { return _enheder; }
        }

        #endregion

        #region Public Methods

        #region Afdeling

        // henter vores midlertidige afdeling
        public Afdeling HentDefaultAfdeling()
        {
            return _afdelinger.First(e => e.AfdelingsId == 1);
        }

        // tjek om der findes en afdeling med et givent navn
        public bool AfdelingEksisterer(string navn)
        {
            return _afdelinger.Exists(e => e.Navn == navn);
        }

        // tilføj afdeling til database, og derefter til collection
        public bool TilføjAfdeling(string navn)
        {
            using (var data = new DataAccess())
            {
                if (!AfdelingEksisterer(navn))
                {
                    // opret afdeling i databasen vha. DataAccess klassen
                    Afdeling afdeling = data.OpretAfdeling(navn);

                    if (afdeling != null)
                    {
                        // tilføj afdelingen til collection
                        _afdelinger.Add(afdeling);
                    }

                    return (afdeling != null);
                }

                return false;
            }
        }

        // redigere en afdeling i databasen og derefter i collection
        public bool RedigerAfdeling(Afdeling afdeling, string navn)
        {
            using (var data = new DataAccess())
            {
                bool redigeret = data.RedigerAfdeling(afdeling, navn);
                if (redigeret)
                {
                    afdeling.Navn = navn;
                }

                return redigeret;
            }
        }

        // fjern en afdeling i databasen og derefter i collection
        public bool FjernAfdeling(Afdeling afdeling)
        {
            using (var data = new DataAccess())
            {
                try
                {
                    data.BeginTransaction();

                    // man må ikke slette den første afdeling.
                    if (afdeling.AfdelingsId == 1) return false;

                    // find afdeling med id'et 1
                    Afdeling midlertidig = _afdelinger.Single(e => e.AfdelingsId == 1);

                    foreach (var bruger in afdeling.Brugere)
                    {
                        // skift brugerens afdeling i databasen
                        data.RedigerBruger(bruger, midlertidig, bruger.Initialer, bruger.Navn);

                        // skift brugerens objekt relation
                        bruger.Afdeling = midlertidig;

                        midlertidig.Brugere.Add(bruger);
                    }

                    bool fjernet = data.FjernAfdeling(afdeling);
                    if (fjernet)
                    {
                        _afdelinger.Remove(afdeling);
                    }

                    data.CommitTransaction();

                    return fjernet;
                }
                catch (Exception e)
                {
                    data.RollBackTransaction();
                    throw e;
                }
            }
        }

        #endregion

        #region Bruger

        // Hent alle brugere fra collection
        public List<Bruger> HentAlleBrugere()
        {
            // iterer igennem hver afdeling, og tag dens brugere
            return _afdelinger.SelectMany(afdeling => afdeling.Brugere).ToList();
        }

        // hent brugere der ikke har lånt en given enhed
        public IEnumerable<Bruger> HentBrugereDerIkkeHarLåntEnhed(Enhed enhed)
        {
            return HentAlleBrugere().Where(e => !e.Udlån.Exists(udlån => udlån.Enhed == enhed));
        }

        // hent alle brugere fra en given afdeling
        public List<Bruger> HentBrugereFraAfdeling(Afdeling afdeling)
        {
            return afdeling.Brugere;
        }

        // hent alle brugere der ikke er fra den givne afdeling
        public IEnumerable<Bruger> HentBrugereIkkeFraAfdeling(Afdeling afdeling)
        {
            return HentAlleBrugere().Where(e => e.Afdeling != afdeling);
        }

        // Tjek om der findes en bruger med samme initialer
        public bool BrugerEksisterer(string initialer)
        {
            foreach (var afdeling in _afdelinger)
            {
                if (afdeling.Brugere.Exists(e => e.Initialer == initialer))
                {
                    return true;
                }
            }
            return false;
        }

        // tilføj bruger til database, og derefter til collection
        public bool TilføjBruger(Afdeling afdeling, string initialer, string navn)
        {
            if (!BrugerEksisterer(initialer))
            {
                using (var data = new DataAccess())
                {
                    // opret ny bruger i databasen
                    Bruger bruger = data.OpretBruger(afdeling, initialer, navn);
                    if (bruger != null)
                    {
                        // tilføj til afdelingens bruger collection
                        afdeling.Brugere.Add(bruger);
                    }

                    return (bruger != null);
                }
            }
            return false;
        }

        // Rediger brugeroplysninger
        public bool RedigerBruger(Bruger bruger, Afdeling afdeling, string initialer, string navn)
        {
            // brugerens nuværende afdeling
            var currentAfdeling = bruger.Afdeling;

            using (var data = new DataAccess())
            {
                // hvis der eksistere en bruger med de nye initialer, 
                // skal det være den samme bruger som er ved at blive ændret
                if (BrugerEksisterer(initialer) && bruger.Initialer != initialer) return false;

                bool redigeret = data.RedigerBruger(bruger, afdeling, initialer, navn);
                // hvis brugeren blev opdateret i databasen
                if (redigeret)
                {
                    // fjern relationer fra og til gamle afdeling
                    currentAfdeling.Brugere.Remove(bruger);

                    // lav relationer til den nye afdeling
                    bruger.Afdeling = afdeling;
                    afdeling.Brugere.Add(bruger);

                    // skift brugerens data
                    bruger.Initialer = initialer;
                    bruger.Navn = navn;
                }
                return redigeret;
            }
        }

        // Fjern bruger
        public bool FjernBruger(Bruger brugerDerSkalFjernes)
        {
            foreach (var afdeling in _afdelinger)
            {
                // for hver bruger i hver afdeling
                foreach (var bruger in afdeling.Brugere)
                {
                    // hvis bruger er lig med den bruger der skal fjernes
                    if (bruger == brugerDerSkalFjernes)
                    {
                        using (var data = new DataAccess())
                        {
                            data.BeginTransaction();

                            // bliver sat til false, hvis der er en fejl med database opdateringer
                            bool success = true;

                            foreach (var udlån in bruger.Udlån)
                            {
                                if (!data.FjernUdlån(udlån)) success = false;
                            }

                            foreach (var historikUdlån in bruger.HistorikUdlån)
                            {
                                if (!data.FjernHistorikUdlån(historikUdlån)) success = false;
                            }

                            if (!data.FjernBruger(bruger)) success = false;

                            if (success)
                            {
                                // fjern udlån fra enhederne
                                bruger.Udlån.ForEach(udlån => udlån.Enhed.Udlån.Remove(udlån));

                                // fjern historikudlån fra enhederne
                                bruger.HistorikUdlån.ForEach(
                                    historikUdlån => historikUdlån.Enhed.HistorikUdlån.Remove(historikUdlån));

                                // fjern bruger fra afdelingen
                                afdeling.Brugere.Remove(bruger);

                                data.CommitTransaction();
                            }
                            else
                            {
                                data.RollBackTransaction();
                            }

                            return success;
                        }
                    }
                }
            }

            // der blev ikke fjernet noget
            return false;
        }

        #endregion

        #region Diverse Enheder

        // opretter diverse enhed i db og derefter i collectionen
        public bool OpretDiverseEnhed(string navn, int totalAntal, DateTime oprettelsesDato)
        {
            using (var data = new DataAccess())
            {
                Diverse diverseEnhed = data.OpretDiverseEnhed(navn, totalAntal, oprettelsesDato);
                if (diverseEnhed != null)
                {
                    _enheder.Add(diverseEnhed);
                }

                return (diverseEnhed != null);
            }
        }

        // fjern en diverse enhed fra databasen, og derefter i collection
        public bool FjernDiverseEnhed(Diverse enhed)
        {
            using (var data = new DataAccess())
            {
                try
                {
                    data.BeginTransaction();

                    // boolean indikator på om database opdateringerne var successfulde.
                    bool @continue = true;

                    bool harSpecifikationer = enhed.Specifikationer.Count > 0;
                    bool harUdlån = enhed.Udlån.Count > 0;
                    bool harHistorik = enhed.HistorikUdlån.Count > 0;

                    // hvis enheden har specifikationer
                    if (harSpecifikationer)
                    {
                        bool fjernet = data.FjernSpecifikationMedEnhed(enhed);
                        if (!fjernet)
                        {
                            @continue = false;
                        }
                    }

                    // hvis enheden har udlån
                    if (harUdlån)
                    {
                        bool fjernet = data.FjernUdlånMedEnhed(enhed);
                        // hvis udlånene ikke blev fjernet
                        if (!fjernet)
                        {
                            @continue = false;
                        }
                    }

                    // hvis enheden har historik
                    if (harHistorik)
                    {
                        bool fjernet = data.FjernHistorikUdlånMedEnhed(enhed);

                        if (!fjernet)
                        {
                            @continue = false;
                        }
                    }

                    bool enhedFjernet = data.FjernDiverseEnhed(enhed);

                    // hvis enheden ikke blev fjernet
                    if (!enhedFjernet)
                    {
                        @continue = false;
                    }

                    // Alle database opdateringer var en success
                    if (@continue)
                    {
                        if (harUdlån)
                        {
                            // iterer igennem hvert udlån tilknyttet den enhed vi sletter
                            foreach (var udlån in enhed.Udlån)
                            {
                                //fjern associationer til de udlån, der var tilknyttet enheden vi slettede
                                udlån.Bruger.Udlån.Remove(udlån);
                            }
                        }

                        if (harHistorik)
                        {
                            foreach (var historikUdlån in enhed.HistorikUdlån)
                            {
                                historikUdlån.Bruger.HistorikUdlån.Remove(historikUdlån);
                            }
                        }

                        @continue = _enheder.Remove(enhed);
                    }

                    if (@continue)
                    {
                        data.CommitTransaction();
                    }
                    else
                    {
                        data.RollBackTransaction();
                    }

                    return @continue;
                }
                catch (Exception)
                {
                    data.RollBackTransaction();
                    return false;
                }
            }
        }

        // opdater en diverse enhed
        public bool OpdaterDiverseEnhed(Diverse diverse, string navn, int antal)
        {
            // tjek om det angivne antal er valid - 
            // antallet må ikke være højere end summen af antal i alle enhedens udlån
            var antalUdlånt = diverse.Udlån.Sum(e => e.Antal);

            // returner false hvis det nye antal ikke er valid
            if (antalUdlånt > antal) return false;

            using (var data = new DataAccess())
            {
                bool opdateret = data.OpdaterDiverseEnhed(diverse, navn, antal);

                if (opdateret)
                {
                    diverse.Navn = navn;
                    diverse.TotalAntal = antal;
                }

                return opdateret;
            }
        }

        // opret specifikation til en diverse enhed
        public bool OpretSpecifikation(Diverse diverseEnhed, string beskrivelse)
        {
            using (var data = new DataAccess())
            {
                Specifikation specifikation = data.OpretSpecifikation(diverseEnhed, beskrivelse);
                if (specifikation != null)
                {
                    diverseEnhed.Specifikationer.Add(specifikation);
                }

                return (specifikation != null);
            }
        }

        // fjern en specifikation fra en diverse enhed
        public bool FjernSpecifikation(Specifikation specifikation)
        {
            using (var data = new DataAccess())
            {
                bool fjerned = data.FjernSpecifikation(specifikation);

                if (fjerned)
                {
                    return specifikation.Diverse.Specifikationer.Remove(specifikation);
                }

                return false;
            }
        }

        // Henter specifikationer til diverse enhed, hvis der ingen specifikationer er
        // bliver der oprettet et objekt til at vise at der ingen specifikationer er valgt
        public List<Specifikation> HentSpecifikationerFraDiverseEnhed(Diverse diverse)
        {
            if (diverse == null || diverse.Specifikationer.Count == 0)
            {
                var list = new List<Specifikation>();
                list.Add(new Specifikation(new Diverse(1, "Test", 1, DateTime.Now), 1, "Ingen specifikation"));
                return list;
            }
            return diverse.Specifikationer;
        }

        #endregion

        #region Computere

        // opretter en ny computer enhed i db og i systemet
        public bool OpretComputerEnhed(string navn, int totalAntal, DateTime oprettelsesDato, string cpu, string hdd, string gpu, string ram, string bundkort, string skærm)
        {
            using (var data = new DataAccess())
            {
                Computer computerEnhed = data.OpretComputerEnhed(navn, totalAntal, oprettelsesDato, cpu, hdd, gpu, ram,
                                                                 bundkort, skærm);
                if (computerEnhed != null)
                {
                    _enheder.Add(computerEnhed);
                }

                return (computerEnhed != null);
            }
        }

        // fjern en eksisterende computer enhed fra db og system
        public bool FjernComputerEnhed(Computer enhed)
        {
            using (var data = new DataAccess())
            {
                try
                {
                    data.BeginTransaction();

                    // boolean indikator på om database opdateringerne var successfulde.
                    bool @continue = true;

                    bool harUdlån = enhed.Udlån.Count > 0;

                    bool harHistorik = enhed.HistorikUdlån.Count > 0;

                    // hvis der er udlån tilknyttet enheden
                    if (harUdlån)
                    {
                        bool fjernet = data.FjernUdlånMedEnhed(enhed);
                        // hvis udlånene ikke blev fjernet
                        if (!fjernet)
                        {
                            @continue = false;
                        }
                    }

                    // computeren har historik?
                    if (harHistorik)
                    {
                        bool fjernet = data.FjernHistorikUdlånMedEnhed(enhed);

                        if (!fjernet)
                        {
                            @continue = false;
                        }
                    }

                    bool enhedFjernet = data.FjernComputerEnhed(enhed);

                    // hvis enheden ikke blev fjernet
                    if (!enhedFjernet)
                    {
                        @continue = false;
                    }

                    // Alle database opdateringer var en success
                    if (@continue)
                    {
                        if (harUdlån)
                        {
                            // iterer igennem hvert udlån tilknyttet den enhed vi sletter
                            foreach (var udlån in enhed.Udlån)
                            {
                                //fjern associationer til de udlån, der var tilknyttet enheden vi slettede
                                udlån.Bruger.Udlån.Remove(udlån);
                            }
                        }

                        if (harHistorik)
                        {
                            foreach (var historikUdlån in enhed.HistorikUdlån)
                            {
                                historikUdlån.Bruger.HistorikUdlån.Remove(historikUdlån);
                            }
                        }

                        @continue = _enheder.Remove(enhed);
                    }

                    if (@continue)
                    {
                        data.CommitTransaction();
                    }
                    else
                    {
                        data.RollBackTransaction();
                    }

                    return @continue;
                }
                catch (Exception)
                {
                    data.RollBackTransaction();
                    return false;
                }
            }
        }

        // opdater en eksisterende computer enhed
        public bool OpdaterComputerEnhed(Computer computer, string navn, string cpu, string hdd, string gpu, string ram, string bundkort, string skærm)
        {
            using (var data = new DataAccess())
            {
                bool opdateret = data.OpdaterComputerEnhed(computer, navn, cpu, hdd, gpu, ram, bundkort, skærm);

                if (opdateret)
                {
                    computer.Navn = navn;
                    computer.CPU = cpu;
                    computer.HDD = hdd;
                    computer.GPU = gpu;
                    computer.RAM = ram;
                    computer.Bundkort = bundkort;
                    computer.Skærm = skærm;
                }

                return opdateret;
            }
        }

        #endregion

        #region Mobile Enheder

        // opretter en mobil enhed i db og i systemet
        public bool OpretMobilEnhed(string navn, int antal, DateTime oprettelsesDato, string mobilNummer, string imeiNummer)
        {
            // tjek at mobilnummeret og IMEI nummeret ikke eksistere i systemet
            if (!MobilNummerEksisterer(mobilNummer) && !ImeiNummerEksisterer(imeiNummer))
            {
                using (var data = new DataAccess())
                {
                    Mobil mobilEnhed = data.OpretMobilEnhed(navn, antal, oprettelsesDato, mobilNummer, imeiNummer);

                    if (mobilEnhed != null)
                    {
                        _enheder.Add(mobilEnhed);
                    }

                    return (mobilEnhed != null);
                }
            }
            return false;
        }

        // opdater en eksisterende mobil enhed i db og system
        public bool OpdaterMobilEnhed(Mobil mobil, string navn, string mobilNummer, string imeiNummer)
        {
            using (var data = new DataAccess())
            {
                bool opdateret = data.OpdaterMobilEnhed(mobil, navn, mobilNummer, imeiNummer);

                if (opdateret)
                {
                    mobil.Navn = navn;
                    mobil.MobilNummer = mobilNummer;
                    mobil.ImeiNummer = imeiNummer;
                }

                return opdateret;
            }
        }

        // fjern en eksisterende mobil enhed fra db og systemet
        public bool FjernMobilEnhed(Mobil enhed)
        {
            using (var data = new DataAccess())
            {
                try
                {
                    data.BeginTransaction();

                    // boolean indikator på om database opdateringerne var successfulde.
                    bool @continue = true;

                    bool harUdlån = enhed.Udlån.Count > 0;

                    bool harHistorik = enhed.HistorikUdlån.Count > 0;

                    // hvis der er udlån tilknyttet enheden
                    if (harUdlån)
                    {
                        bool fjernet = data.FjernUdlånMedEnhed(enhed);
                        // hvis udlånene ikke blev fjernet
                        if (!fjernet)
                        {
                            @continue = false;
                        }
                    }
                    

                    // mobilen har historik?
                    if (harHistorik)
                    {
                        bool fjernet = data.FjernHistorikUdlånMedEnhed(enhed);

                        if (!fjernet)
                        {
                            @continue = false;
                        }
                    }

                    bool enhedFjernet = data.FjernMobilEnhed(enhed);

                    // hvis enheden ikke blev fjernet
                    if (!enhedFjernet)
                    {
                        @continue = false;
                    }

                    // Alle database opdateringer var en success
                    if (@continue)
                    {
                        if (harUdlån)
                        {
                            // iterer igennem hvert udlån tilknyttet den enhed vi sletter
                            foreach (var udlån in enhed.Udlån)
                            {
                                //fjern associationer til de udlån, der var tilknyttet enheden vi slettede
                                udlån.Bruger.Udlån.Remove(udlån);
                            }
                        }

                        if (harHistorik)
                        {
                            foreach (var historikUdlån in enhed.HistorikUdlån)
                            {
                                historikUdlån.Bruger.HistorikUdlån.Remove(historikUdlån);
                            }
                        }

                        @continue = _enheder.Remove(enhed);
                    }

                    if (@continue)
                    {
                        data.CommitTransaction();
                    }
                    else
                    {
                        data.RollBackTransaction();
                    }

                    return @continue;
                }
                catch (Exception)
                {
                    data.RollBackTransaction();
                    return false;
                }
            }
        }

        // tjek om et givent mobil nummer eksisterer
        // hvis det gør returner true, ellers false
        public bool MobilNummerEksisterer(string mobilNummer)
        {
            foreach (var enhed in _enheder.Where(e => e is Mobil).Cast<Mobil>())
            {
                if (enhed.MobilNummer == mobilNummer) return true;
            }
            return false;
        }

        // tjek om et givent imei nummer eksisterer
        // hvis det gør returner true, ellers false
        public bool ImeiNummerEksisterer(string imeiNummer)
        {
            foreach (var enhed in _enheder.Where(e => e is Mobil).Cast<Mobil>())
            {
                if (enhed.ImeiNummer == imeiNummer) return true;
            }
            return false;
        }

        #endregion

        #region Software

        // her finder vi de computere der har lånt et stykke software,
        // find alle udlån på et stykke software, og find computerne den vej
        public IEnumerable<Computer> HentComputereMedSoftware(Software software)
        {
            // finder alle produktnøgler på det valgte stykke software
            List<Produktnøgle> produktNøgler = software.ProduktNøgler;

            foreach (Produktnøgle nøgle in produktNøgler)
            {
                // for hvert udlån produktnøglen har tilknyttet
                foreach (SoftwareUdlån udlån in nøgle.SoftwareUdlån)
                {
                    // yield return udlånets computer
                    yield return udlån.Computer;
                }
            }
        }

        // tilføj et nyt stykke software til db og systemet
        public bool TilføjSoftware(string navn, DateTime dato)
        {
            using (var data = new DataAccess())
            {
                Software software = data.OpretSoftware(navn, dato);

                if (software != null)
                {
                    _software.Add(software);
                }

                return (software != null);
            }
        }

        // opdater et givent stykke software i db og systemet
        public bool OpdaterSoftware(Software software, string navn, DateTime dato)
        {
            using (var data = new DataAccess())
            {
                bool opdateret = data.OpdaterSoftware(software, navn, dato);

                if (opdateret)
                {
                    software.Navn = navn;
                    software.Oprettelsesdato = dato;
                }

                return opdateret;
            }
        }

        // fjern et givent stykke software fra db og fra systemet
        public bool FjernSoftware(Software software)
        {
            using (var data = new DataAccess())
            {
                data.BeginTransaction();

                // bliver false hvis et databasekald fejler
                bool success = true;

                // hvis softwaret har produktnøgler
                if (software.ProduktNøgler.Count > 0)
                {
                    foreach (var nøgle in software.ProduktNøgler)
                    {
                        if (nøgle.SoftwareUdlån.Count > 0)
                        {
                            bool udlånFjernet = data.FjernSoftwareUdlånMedProduktnøgle(nøgle);
                            if (!udlånFjernet) success = false;
                        }
                    }

                    // fjern nøgler fra database
                    bool nøglerFjernet = data.FjernProduktnøglerMedSoftware(software);
                    if (!nøglerFjernet) success = false;
                }

                // fjern software fra database
                bool softwareFjernet = data.FjernSoftware(software);
                if (!softwareFjernet) success = false;

                // alle database kald gik igennem
                if (success)
                {
                    data.CommitTransaction();

                    //fjern objekt associationer
                    foreach (var nøgle in software.ProduktNøgler)
                    {
                        foreach (var udlån in nøgle.SoftwareUdlån)
                        {
                            // fjern referencer til computere
                            udlån.Computer.SoftwareUdlån.Remove(udlån);
                        }
                    }

                    // fjern software objektet
                    _software.Remove(software);
                }
                else
                {
                    data.RollBackTransaction();
                }

                return success;
            }
        }

        // tilføj en ny produktnøgle til db og systemet
        public bool TilføjProduktnøgle(Software selectedSoftware, string nøgle, DateTime dato, int antal)
        {
            using (var data = new DataAccess())
            {
                var produktNøgle = data.OpretProduktnøgle(selectedSoftware, nøgle, dato, antal);
                if (produktNøgle != null)
                {
                    produktNøgle.Software = selectedSoftware;
                    selectedSoftware.ProduktNøgler.Add(produktNøgle);
                }

                return produktNøgle != null;
            }
        }

        // opdater en eksisterende produktnøgle i db og systemet
        public bool OpdaterProduktnøgle(Produktnøgle produktNøgle, DateTime dato, int antal)
        {
            using (var data = new DataAccess())
            {
                bool opdateret = data.OpdaterProduktnøgle(produktNøgle, dato, antal);
                if (opdateret)
                {
                    produktNøgle.Udløbsdato = dato;
                    produktNøgle.Antal = antal;
                }

                return opdateret;
            }
        }

        // fjern en eksisterende produktnøgle fra db og systemet
        public bool FjernProduktnøgle(Produktnøgle produktnøgle)
        {
            using (var data = new DataAccess())
            {
                data.BeginTransaction();

                bool success = true;

                // er der udlån på produktnøglen?
                if (produktnøgle.SoftwareUdlån.Count > 0)
                {
                    bool fjernSoftwareUdlån = data.FjernSoftwareUdlånMedProduktnøgle(produktnøgle);
                    if (!fjernSoftwareUdlån) success = false;
                }

                // fjern produktnøglen fra databasen
                bool fjernProduktnøgle = data.FjernProduktnøgle(produktnøgle);
                if (!fjernProduktnøgle) success = false;

                if (success)
                {
                    data.CommitTransaction();

                    // fjern software udlån fra computere
                    produktnøgle.SoftwareUdlån.ForEach(e => e.Computer.SoftwareUdlån.Remove(e));

                    // fjern produktnøglen selv fra programmet
                    produktnøgle.Software.ProduktNøgler.Remove(produktnøgle);
                }
                else
                {
                    data.RollBackTransaction();
                }

                return success;
            }
        }

        // henter alle produktnøgler der stadig kan bruges til installationer, til et givent stykke software
        public IEnumerable<IProduktnøgle> HentLedigeProduktnøglerMedSoftware(Software software)
        {
            return software.ProduktNøgler.Where(e => e.Antal > e.AntalUdlånt);
        }

        // hent alle softwareudlån til en given produktnøgle
        public IEnumerable<ISoftwareUdlån> HentUdlånMedProduktnøgle(IProduktnøgle nøgle)
        {
            return
                AltSoftware.SelectMany(
                    e => e.ProduktNøgler.Where(p => p == nøgle).SelectMany(s => s.SoftwareUdlån));
        }

        // henter alle computere der ikke har lånt et givent stykke software
        public IEnumerable<Computer> HentComputereUdenSoftwareUdlån(Software software)
        {
            return AlleComputerEnheder.Where(
                computer => !(computer.SoftwareUdlån.Any(udlån => udlån.Produktnøgle.Software == software)));
        }

        // henter alle computere der har installeret en given produktnøgle
        public IEnumerable<Computer> HentComputereMedProduktnøgle(Produktnøgle nøgle)
        {
            return AlleComputerEnheder.Where(w => (w.SoftwareUdlån.Any(a => a.Produktnøgle == nøgle)));
        }

        // opret et nyt stykke software i db og systemet
        public bool OpretSoftwareUdlån(Computer computer, Produktnøgle nøgle, DateTime dato)
        {
            using (var data = new DataAccess())
            {
                SoftwareUdlån udlån = data.OpretSoftwareUdlån(computer, nøgle, dato);

                if (udlån != null)
                {
                    // lav objekt relationer

                    computer.SoftwareUdlån.Add(udlån);
                    nøgle.SoftwareUdlån.Add(udlån);

                    udlån.Computer = computer;
                    udlån.Produktnøgle = nøgle;
                }

                return udlån != null;
            }
        }

        // fjern et givent softwareudlån fra db og system
        public bool FjernSoftwareUdlån(Computer computer, Software selectedSoftware)
        {
            // find det software udlån der skal slettes
            SoftwareUdlån udlån = computer.SoftwareUdlån.SingleOrDefault(e => e.Produktnøgle.Software == selectedSoftware);

            using (var data = new DataAccess())
            {
                // fjern software udlån fra databasen
                bool slettet = data.FjernSoftwareUdlån(udlån.Computer, udlån.Produktnøgle);

                if (slettet)
                {
                    // fjern objekt relationer
                    udlån.Computer.SoftwareUdlån.Remove(udlån);
                    udlån.Produktnøgle.SoftwareUdlån.Remove(udlån);
                }

                return slettet;
            }
        }

        #endregion

        #region Udlån

        // Opretter udlån i db og derefter i collection
        public bool OpretUdlån(Bruger bruger, Enhed enhed, int antal, DateTime dato)
        {
            // hvis enheden er af typen Mobil eller Computer
            if (enhed is Computer || enhed is Mobil)
            {
                // antal på Mobil og Computer objekter skal være 1
                if (antal != 1) return false;
            }

            // valider at antallet der ønskes udlånt, er til rådighed
            int antalLedigeEnheder = enhed.TotalAntal - enhed.Udlån.Sum(e => e.Antal);

            // der var ikke nok ledige enheder
            if (antal > antalLedigeEnheder) return false;       

            using (var data = new DataAccess())
            {
                Udlån udlån = data.OpretUdlån(bruger.BrugerId, enhed.EnhedsId, antal, dato);
                if (udlån != null)
                {
                    

                    bruger.Udlån.Add(udlån); // tilknyt udlån til brugeren
                    enhed.Udlån.Add(udlån); // tilknyt udlån til enheden

                    udlån.Bruger = bruger; // tilknyt bruger til udlånet
                    udlån.Enhed = enhed; // tilknyt enhed til udlånet

                    // tilføj historik
                    HistorikUdlån historikUdlån = data.HistorikOpretUdlån(bruger.BrugerId, enhed.EnhedsId, antal, dato);
                    enhed.HistorikUdlån.Add(historikUdlån);
                    bruger.HistorikUdlån.Add(historikUdlån);
                    historikUdlån.Bruger = bruger;
                    historikUdlån.Enhed = enhed;
                }
                return (udlån != null);
            }
        }

        // fjern et udlån fra database og systemet
        public bool FjernUdlån(Udlån udlån)
        {
            using (var data = new DataAccess())
            {
                // fjern udlån i databasen
                bool fjernet = data.FjernUdlån(udlån);

                // udlånet blev slettet fra db
                if (fjernet)
                {
                    // fjern associationer til udlånets bruger
                    udlån.Bruger.Udlån.Remove(udlån);

                    // fjern associationer til udlånets enhed
                    udlån.Enhed.Udlån.Remove(udlån);

                    // find udlånets historik objekt
                    var historik =
                        udlån.Bruger.HistorikUdlån.SingleOrDefault(e => e.EnhedsId == udlån.EnhedsId &&
                                                                        e.BrugerId == udlån.BrugerId &&
                                                                        e.SlutDato == null);
                    // da udlånet er fjernet, opdatere vi historikken på udlånet til at have en slutdato
                    RedigerHistorikUdlån(historik, DateTime.Now);
                }

                return fjernet;
            }
        }

        // opdater et givent udlån i db og systemet
        public bool OpdaterUdlån(Udlån udlån, int antal, DateTime dato)
        {
            // valider at antallet der ønskes udlånt, er til rådighed
            Enhed enhed = udlån.Enhed;
            int antalLedigeEnheder = enhed.TotalAntal - enhed.Udlån.Sum(e => e.Antal);

            // der var ikke nok ledige enheder
            if (antal > antalLedigeEnheder + udlån.Antal) return false;

            using (var data = new DataAccess())
            {
                // opdater udlån i databasen
                bool updated = data.OpdaterUdlån(udlån, antal, dato);

                if (updated)
                {
                    udlån.Antal = antal;
                    udlån.UdlånsDato = dato;
                }

                return updated;
            }
        }

        // rediger et historik objekt i database og systemet
        public bool RedigerHistorikUdlån(HistorikUdlån historikUdlån, DateTime slutDato)
        {
            using (var data = new DataAccess())
            {
                // rediger historik i database
                bool updated = data.RedigerHistorikUdlån(historikUdlån, slutDato);

                if (updated)
                {
                    historikUdlån.SlutDato = slutDato;
                }
                return updated;
            }
        }

        // fjern et historik objekt fra database og systemet
        public bool FjernHistorikUdlån(HistorikUdlån historikUdlån)
        {
            using (var data = new DataAccess())
            {
                // fjern historik fra database
                bool fjernet = data.FjernHistorikUdlån(historikUdlån);

                if (fjernet)
                {
                    // fjern associationer til historik udlånets bruger
                    historikUdlån.Bruger.HistorikUdlån.Remove(historikUdlån);

                    // fjern associationer til historik udlånets enhed
                    historikUdlån.Enhed.HistorikUdlån.Remove(historikUdlån);
                }
                return fjernet;
            }
        }

        #endregion

        #endregion

        #region Opstart

        // laver alle objektet associationer når programmet opstarter
        private void LavObjektTilknytninger()
        {
            using (var data = new DataAccess())
            {
                // Hent alle enheder fra db og opdater vores collection
                _enheder = data.HentAlleEnheder().ToList();

                // for hver diverseenhed, tilknyt liste med specifikationer
                foreach (var enhed in _enheder.Where(e => e is Diverse).Cast<Diverse>())
                {
                    enhed.Specifikationer = new List<Specifikation>(data.HentSpecifikationerFraDiverseEnhed(enhed));
                }

                // find diverse enheder, og tilknyt deres specifikationer
                foreach (Enhed enhed in _enheder)
                {
                    if (enhed is Diverse)
                    {
                        var diverse = enhed as Diverse;
                        diverse.Specifikationer = data.HentSpecifikationerFraDiverseEnhed(diverse).ToList();
                    }
                }

                // hent alle afdelinger fra db og opdater vores collection
                _afdelinger = new List<Afdeling>(data.HentAlleAfdelinger());

                // for hver afdeling, tilknyt dens brugere
                foreach (var afdeling in _afdelinger)
                {
                    afdeling.Brugere = new List<Bruger>(data.HentBrugereMedAfdeling(afdeling));
                }

                // lav udlånstilknytninger
                foreach (var udlån in data.HentAlleUdlån())
                {
                    //tilknyt hver udlån til sin bruger, begge veje
                    foreach (var bruger in HentAlleBrugere())
                    {
                        if (udlån.BrugerId == bruger.BrugerId)
                        {
                            bruger.Udlån.Add(udlån);
                            udlån.Bruger = bruger;
                        }
                    }

                    // tilknyt hver udlån til sin enhed, begge veje
                    foreach (var enhed in _enheder)
                    {
                        if (udlån.EnhedsId == enhed.EnhedsId)
                        {
                            udlån.Enhed = enhed;
                            enhed.Udlån.Add(udlån);
                        }
                    }
                }

                // lav historikudlånstilknytninger
                foreach (var historikUdlån in data.HentAlleHistorikUdlån())
                {
                    //tilknyt hver historikudlån til sin bruger, begge veje
                    foreach (var bruger in HentAlleBrugere())
                    {
                        if (historikUdlån.BrugerId == bruger.BrugerId)
                        {
                            bruger.HistorikUdlån.Add(historikUdlån);
                            historikUdlån.Bruger = bruger;
                        }
                    }
                    // tilknyt hver historikudlån til sin enhed, begge veje
                    foreach (var enhed in _enheder)
                    {
                        if (historikUdlån.EnhedsId == enhed.EnhedsId)
                        {
                            enhed.HistorikUdlån.Add(historikUdlån);
                            historikUdlån.Enhed = enhed;
                        }
                    }
                }

                // henter alt software
                _software = new List<Software>(data.HentAltSoftware());

                // tilknyt produktnøgler til hvert stykke software
                foreach (var software in _software)
                {
                    software.ProduktNøgler = data.HentProduktnøglerMedSoftware(software).ToList();
                    software.ProduktNøgler.ForEach(e => e.Software = software);
                }

                // lav softwareudlån tilknytninger
                foreach (var udlån in data.HentAlleSoftwareUdlån())
                {
                    // find de enheder af typen Computer, og typecast dem
                    var computere = _enheder.Where(enhed => enhed is Computer).Cast<Computer>();

                    // for hvert stykke software udlån find computere og lav tilknytninger
                    foreach (var computer in computere)
                    {
                        if (computer.EnhedsId == udlån.EnhedsId)
                        {
                            udlån.Computer = computer;
                            computer.SoftwareUdlån.Add(udlån);
                        }
                    }

                    // for hvert stykke softwareudlån, find software og lav tilknytninger
                    foreach (var software in _software)
                    {
                        // tilknytning til softwareudlån laves på softwares produktnøgler
                        foreach (var nøgle in software.ProduktNøgler)
                        {
                            if (nøgle.ProduktnøgleId == udlån.ProduktnøgleId)
                            {
                                udlån.Produktnøgle = nøgle;
                                nøgle.SoftwareUdlån.Add(udlån);
                            }
                        }
                    }
                }
            }
        }

        #endregion

    }
}
